package lion.bugrambo.execution;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import lion.bugrambo.model.Bug;
import lion.bugrambo.model.Project;
import lion.bugrambo.model.TestCase;
import lion.bugrambo.model.TestCaseTemplate;
import lion.bugrambo.model.TestGroup;
import lion.bugrambo.model.TestPoint;
import lion.dev.lang.KV;
import lion.dev.lang.Lang;
import lion.dev.lang.MapJ;
import lion.dev.text.Formater;
import lion.dev.web.Validator;
import lion.dev.web.paging.PagedVo;
import lion.framework.core.db.DB;
import lion.framework.core.db.DataTable;
import lion.framework.core.db.SQLJoin;
import lion.framework.core.db.SessionFactory;
import lion.framework.core.db.paging.SQLPagingFactory;
import lion.framework.core.web.anno.Namespace;
import lion.framework.core.web.anno.Path;
import lion.framework.core.web.anno.Result;
import lion.framework.core.web.anno.View;
import lion.framework.core.web.exception.WebException;
import lion.framework.core.web.request.Request;
import lion.framework.core.web.views.ResultType;
import lion.web.platform.priv.user.LoginUser;

/**
 * 
 * 测试管理
 * 
 * @author lion
 * @email hl_0074@sina.com
 * @date 2015年6月26日
 */
@Namespace("#{admin}/br")
public class TestingExecuting {

	@Path("/testing")
	@Result(@View("/WEB-INF/template/bugrambo/testing/index.jsp"))
	public String index() {

		return "success";
	}

	@Path("/testing/export")
	@Result(@View("/WEB-INF/template/bugrambo/testing/template_html.jsp"))
	public String export(Request request) {

		int groupId = request.getInt("id");
		TestGroup group = TestGroup.load(groupId);
		if (group == null) { throw new WebException("不存在的测试组"); }

		request.addModel("group", group);

		Project project = Project.load(group.getProjectId());
		if (project == null) { throw new WebException("不存在的测试项目"); }

		request.addModel("project", project);

		int pointNum = SessionFactory.getCurrentDB().queryInt("select count(*) from TestPoint where groupId=?", group.getId());
		request.addModel("pointNum", pointNum);

		int caseNum = SessionFactory.getCurrentDB()
				.queryInt("select count(*) from TestCase tc where exists (select 1 from TestPoint tp where tp.id =tc.testPointId and tp.groupId=?)", group.getId());
		int passNum = SessionFactory.getCurrentDB()
				.queryInt("select count(*) from TestCase tc where result = 1 and exists (select 1 from TestPoint tp where tp.id =tc.testPointId and tp.groupId=?)", group.getId());

		request.addModel("caseNum", caseNum);
		request.addModel("passNum", passNum);
		request.addModel("passRate", Formater.formateNumber("###.00", passNum * 1.0 / caseNum * 100));

		String sql = "select realName from User u,TestCase tc where tc.testUser=u.id and exists (select 1 from TestPoint tp where tp.id=tc.testPointId and tp.groupId=?) group by u.id";

		List<String> names = SessionFactory.getCurrentDB().queryCol(sql, group.getId());

		request.addModel("testusers", StringUtils.join(names, ","));

		// get modules

		List<MapJ> modules = SessionFactory.getCurrentDB()
				.query("select pm.id,pm.name from ProjectModel pm where exists(select 1 from TestPoint tp where pm.id = tp.moduleId and tp.groupId=? )", group.getId()).getRows();
		for (MapJ model : modules) {

			List<MapJ> points = SessionFactory.getCurrentDB().query("select id,name from TestPoint where groupId=? and moduleId=?", group.getId(), model.getInt("id")).getRows();

			List<MapJ> nPoints = new ArrayList<>();
			for (MapJ point : points) {
				List<MapJ> cases = SessionFactory.getCurrentDB()
						.query("select tc.*,(select realName from User u where u.id=tc.testUser ) testUserName from TestCase tc where testPointId=?", point.getInt("id")).getRows();
				if (cases.size() > 0) {
					point.put("cases", cases);
					nPoints.add(point);
				}
			}
			if (nPoints.size() > 0) {
				model.put("points", nPoints);
			}
		}
		request.addModel("modules", modules);
		return "success";
	}

	// test point
	@Path("/testing/tp")
	@Result(@View("/WEB-INF/template/bugrambo/testing/tp.jsp"))
	public String testPoint(Request request) {

		int tg = request.getInt("tg");
		if (tg > 0) {
			TestGroup tGroup = TestGroup.load(tg);
			if (tGroup != null) {
				request.addModel("project", Project.load(tGroup.getProjectId()));
				request.addModel("tGroup", tGroup);
			}
		}

		return "success";
	}

	@Path("/testing/tp/save")
	@Result(@View(type = ResultType.JSON))
	public String savePoint(Request request) {

		String name = request.getString("name");
		if (StringUtils.isBlank(name)) {
			request.addError("msg", "名称不能为空");
			return "success";
		}

		TestPoint tp = new TestPoint();
		tp.setCreateTime(new Date());
		tp.setCreator(LoginUser.current().getUser().getId());
		tp.setModuleId(request.getInt("moduleId"));
		tp.setProjectId(SessionFactory.getCurrentDB().queryInt("select projectId from TestGroup where id=:groupId", request));
		tp.setGroupId(request.getInt("groupId"));
		tp.setName(name);

		tp.save();

		return "success";
	}

	@Path("/testing/tp/update")
	@Result(@View(type = ResultType.JSON))
	public String updatePoint(Request request) {

		String name = request.getString("name");
		if (StringUtils.isBlank(name)) {
			request.addError("msg", "名称不能为空");
			return "success";
		}

		SessionFactory.getCurrentDB().update(TestPoint.TABLE_NAME, new KV<String, Integer>("id", request.getInt("id")), new KV<String, String>("name", name));

		return "success";
	}

	@Path("/testing/tp/delete")
	@Result(@View(type = ResultType.JSON))
	public String deletePoint(Request request) {

		int[] ids = Validator.toIntArray(request.getString("ids"));
		if (ids.length > 0) {

			List<String> sqls = new ArrayList<>();
			for (int id : ids) {
				sqls.add("delete from TestCase where testPointId=" + id);
				sqls.add("delete from TestPoint where id=" + id);
			}
			SessionFactory.getCurrentDB().updateBatch(sqls.toArray(new String[0]));
		}
		return "success";
	}

	@Path("/testing/tp/fix")
	@Result(@View(type = ResultType.JSON))
	public String fixPoint(Request request) {

		int[] ids = Validator.toIntArray(request.getString("ids"));

		if (ids.length > 0) {
			SessionFactory.getCurrentDB()
					.update("update TestPoint tp set total=(select count(*) from TestCase where testPointId=tp.id),p=(select count(*) from TestCase where testPointId=tp.id and result=1),f=(select count(*) from TestCase where testPointId=tp.id and result=2) where tp.id in("
							+ Lang.join(ids) + ")");
		}

		return "success";
	}

	@Path("/testing/tp/list")
	@Result(@View(type = ResultType.JSON))
	public String listTestPoint(Request request) {

		int page = request.getInt("page", 1);
		int size = request.getInt("size", 20);

		SQLJoin sqlJoin = new SQLJoin();
		sqlJoin.add("groupId=:groupId");
		if (request.getInt("moduleId") > 0) {
			sqlJoin.add("moduleId=:moduleId");
		}

		PagedVo<TestPoint> pagedVo = new PagedVo<>(TestPoint.count("where " + sqlJoin.toString(), request));

		pagedVo.getPaging().setPageNumber(page);
		pagedVo.getPaging().setPageSize(size);

		pagedVo.setVos(
				TestPoint.list(pagedVo.getPaging().getStartRowNumber(), pagedVo.getPaging().getPageSize(), "where " + sqlJoin.toString(), request, SessionFactory.getCurrentDB()));

		request.addModel("json", pagedVo);

		return "success";
	}

	// test case
	@Path("/testing/case/edit")
	@Result(@View("/WEB-INF/template/bugrambo/testing/editcase.jsp"))
	public String editCase(Request request) {

		int id = request.getInt("id");
		int testPointId = request.getInt("testPointId");
		if (id > 0) {
			TestCase tc = TestCase.load(id);
			if (tc != null) {
				request.addModel("tcase", tc);
				testPointId = tc.getTestPointId();
			}
		}
		request.addModel("testPointId", testPointId);

		return "success";
	}

	@Path("/testing/case/list")
	@Result(@View(type = ResultType.JSON))
	public String listCase(Request request) {

		int page = request.getInt("page", 1);
		int size = request.getInt("size", 20);

		String sql = "where testPointId=:testPointId";
		if (request.getInt("id") > 0) {
			sql += " and id=:id";
		}

		PagedVo<TestCase> pagedVo = new PagedVo<>(TestCase.count(sql, request));

		pagedVo.getPaging().setPageNumber(page);
		pagedVo.getPaging().setPageSize(size);

		pagedVo.setVos(
				TestCase.list(pagedVo.getPaging().getStartRowNumber(), pagedVo.getPaging().getPageSize(), sql + " order by id desc", request, SessionFactory.getCurrentDB()));

		request.addModel("json", pagedVo);

		return "success";
	}

	@Path("/testing/case/save")
	public void saveCase(Request request) {

		TestCase testCase = TestCase.create(request);
		if (testCase.getInput() == null) {
			testCase.setInput(testCase.getName());
		}
		testCase.setCreateTime(new Date());
		testCase.setCreator(LoginUser.current().getUser().getId());

		testCase.save();

		SessionFactory.getCurrentDB().update("update TestPoint set total=total+1 where id=?", testCase.getTestPointId());
	}

	@Path("/testing/case/update")
	public void updateCase(Request request) {

		TestCase testCase = TestCase.create(request);

		TestCase old = TestCase.load(testCase.getId());
		if (old == null) {
			request.addError("msg", "测试用例不存在");
			return;
		}
		testCase.setCreateTime(old.getCreateTime());
		testCase.setCreator(old.getCreator());

		testCase.update();

		if (testCase.getResult() != old.getResult()) {

			StringBuffer sql = new StringBuffer();
			if (old.getResult() == 1) {
				sql.append(",p=p-1");
			} else if (old.getResult() == 2) {
				sql.append(",f=f-1");
			}
			if (testCase.getResult() == 1) {
				sql.append(",p=p+1");
			} else if (testCase.getResult() == 2) {
				sql.append(",f=f+1");
			}

			SessionFactory.getCurrentDB().update("update TestPoint set " + StringUtils.stripStart(sql.toString(), ",") + " where id=?", testCase.getTestPointId());
		}
	}

	@Path("/testing/case/delete")
	public void deleteCase(Request request) {

		int[] ids = Validator.toIntArray(request.getString("ids"));

		System.out.println(Arrays.toString(ids));

		List<String> sqls = new ArrayList<>();
		for (int id : ids) {
			StringBuffer sql = new StringBuffer();
			sql.append("total=total-1");
			TestCase tcase = TestCase.load(id);
			if (tcase.getResult() == 1) {
				sql.append(",p=p-1");
			} else if (tcase.getResult() == 2) {
				sql.append(",f=f-1");
			}
			sqls.add("update TestPoint set " + sql.toString() + " where id=" + tcase.getTestPointId());

			System.out.println("update TestPoint set " + sql.toString() + " where id=" + tcase.getTestPointId());

			sqls.add("delete from TestCase where id=" + id);
		}
		SessionFactory.getCurrentDB().updateBatch(sqls.toArray(new String[0]));
	}

	@Path("/testing/case/pass")
	public void passCase(Request request) {

		int id = request.getInt("id");

		TestCase tc = TestCase.load(id);
		if (tc == null) { return; }
		int r = tc.getResult();
		tc.setResult(1);
		tc.setTestUser(LoginUser.current().getUser().getId());
		tc.setTestTime(new Date());
		if (StringUtils.isBlank(tc.getActual())) {
			tc.setActual(tc.getExpected());
		}
		tc.update();

		String sql = "p=p+1";
		if (r == 2) {
			sql += ",f=f-1";
		}
		SessionFactory.getCurrentDB().update("update TestPoint set " + sql + " where id=?", tc.getTestPointId());
	}

	@Path("/testing/case/fail")
	public void failCase(Request request) {

		int id = request.getInt("id");

		TestCase tc = TestCase.load(id);
		if (tc == null) { return; }
		int r = tc.getResult();
		tc.setResult(2);
		tc.setTestUser(LoginUser.current().getUser().getId());
		tc.setTestTime(new Date());
		tc.update();

		String sql = "f=f+1";
		if (r == 1) {
			sql += ",p=p-1";
		}
		SessionFactory.getCurrentDB().update("update TestPoint set " + sql + " where id=?", tc.getTestPointId());
	}

	@Path("/testing/case/reportbug")
	public void reportCaseBug(Request request) {

		int id = request.getInt("id");
		if (id < 0) { return; }

		TestCase tc = TestCase.load(id);
		if (tc == null) { return; }
		TestPoint tp = TestPoint.load(tc.getTestPointId());
		if (tp == null) { return; }

		TestGroup tg = TestGroup.load(tp.getGroupId());
		if (tg == null) { return; }

		Bug bug = new Bug();
		bug.setCreateTime(new Date());
		bug.setCreator(LoginUser.current().getUser().getId());
		bug.setBugStatus(1);
		bug.setModelId(tp.getModuleId());
		bug.setProjectId(tp.getProjectId());
		bug.setCreateBuild(tg.getVersion());
		bug.setTitle(tp.getName() + "/" + tc.getName());

		StringBuilder sb = new StringBuilder();
		sb.append("\nTest Case：ID[" + tc.getId() + "]" + tg.getName() + "/" + tp.getName() + "/" + tc.getName());
		if (StringUtils.isNotBlank(tc.getExpected())) {
			sb.append("\nExpected:" + tc.getExpected());
		}
		if (StringUtils.isNotBlank(tc.getActual())) {
			sb.append("\nActual:" + tc.getActual());
		}
		if (StringUtils.isNotBlank(tc.getInput())) {
			sb.append("\nInput:" + tc.getInput());
		}
		if (StringUtils.isNotBlank(tc.getRemark())) {
			sb.append("Remark:" + tc.getRemark());
		}
		bug.setDescription(sb.toString());

		bug.save();
	}

	@Path("/testing/case/template")
	@Result(@View("/WEB-INF/template/bugrambo/testing/casetemplate.jsp"))
	public String caseTemplate(Request request) {

		return "success";
	}

	@Path("/testing/case/template/edit")
	@Result(@View("/WEB-INF/template/bugrambo/testing/editcasetemplate.jsp"))
	public String editCaseTemplate(Request request) {

		int id = request.getInt("id");
		if (id > 0) {
			request.addModel("casetpl", TestCaseTemplate.load(id));
		}

		return "success";
	}

	@Path("/testing/case/template/list")
	@Result(@View(type = ResultType.JSON))
	public String listCaseTemplate(Request request) {

		int page = request.getInt("page", 1);
		int size = request.getInt("size", 20);

		PagedVo<TestCaseTemplate> pagedVo = new PagedVo<>(TestCaseTemplate.count());
		pagedVo.getPaging().setPageNumber(page);
		pagedVo.getPaging().setPageSize(size);

		pagedVo.setVos(TestCaseTemplate.list(pagedVo.getPaging().getStartRowNumber(), pagedVo.getPaging().getPageSize()));

		request.addModel("json", pagedVo);
		return "success";
	}

	@Path("/testing/case/template/save")
	public void saveCaseTemplate(Request request) {

		TestCaseTemplate tcs = TestCaseTemplate.create(request);
		tcs.save();
	}

	@Path("/testing/case/template/update")
	public void updateCaseTemplate(Request request) {

		TestCaseTemplate tcs = TestCaseTemplate.create(request);
		tcs.update();
	}

	@Path("/testing/case/template/delete")
	public void deleteCaseTemplate(Request request) {

		int[] ids = Validator.toIntArray(request.getString("ids"));
		if (ids.length == 0) { return; }
		TestCaseTemplate.delete(ids);
	}

	@Path("/testing/case/template/tocase")
	public void templateToCase(Request request) {

		int[] ids = Validator.toIntArray(request.getString("ids"));
		if (ids.length == 0) { return; }

		int testPointId = request.getInt("testPointId");

		for (int id : ids) {
			TestCaseTemplate tct = TestCaseTemplate.load(id);
			if (tct == null) {
				continue;
			}

			TestCase tc = new TestCase();
			tc.setCreateTime(new Date());
			tc.setCreator(LoginUser.current().getUser().getId());
			tc.setInput(tct.getInput());
			tc.setName(tct.getName());
			tc.setExpected(tct.getExpected());
			tc.setTestPointId(testPointId);

			tc.save();
		}
	}

	// test group
	@Path("/testing/group/list")
	@Result(@View(type = ResultType.JSON))
	public String listGroup(Request request) {

		int page = request.getInt("page");
		int size = request.getInt("size");

		int projectId = request.getInt("projectId");

		PagedVo<TestGroup> pagedVo = new PagedVo<>(TestGroup.count("where projectId=" + projectId));
		pagedVo.getPaging().setPageNumber(page);
		pagedVo.getPaging().setPageSize(size);

		String sql = "select tg.*,(select realName from User u where u.id = tg.creator ) creatorName from TestGroup tg where projectId=" + projectId + " order by id desc";

		DB db = SessionFactory.getCurrentDB();
		DataTable dt = db.query(SQLPagingFactory.paging(db.getType(), sql, pagedVo.getPaging().getStartRowNumber(), pagedVo.getPaging().getPageSize()));

		for (MapJ row : dt.getRows()) {
			row.put("startTime", Formater.formatDate("yyyy-MM-dd", row.getDate("startTime")));
			row.put("endTime", Formater.formatDate("yyyy-MM-dd", row.getDate("endTime")));
		}

		request.addModel("vos", dt.getRows());
		request.addModel("paging", pagedVo.getPaging());

		return "success";
	}

	@Path("/testing/group/edit")
	@Result(@View("/WEB-INF/template/bugrambo/testing/editGroup.jsp"))
	public String editGroup(Request request) {

		int id = request.getInt("id");
		int projectId = request.getInt("projectId");
		if (id > 0) {
			TestGroup tg = TestGroup.load(id);
			request.addModel("tgroup", tg);
			if (tg != null) {
				projectId = tg.getProjectId();
			}
		}
		if (projectId > 0) {
			request.addModel("project", Project.load(projectId));
		}

		return "success";
	}

	@Path("/testing/group/save")
	public void saveGroup(Request request) {

		TestGroup tg = TestGroup.create(request);
		tg.setCreateTime(new Date());
		tg.setCreator(LoginUser.current().getUser().getId());

		tg.save();
	}

	@Path("/testing/group/update")
	public void updateGroup(Request request) {

		TestGroup tg = TestGroup.create(request);
		TestGroup oldTg = TestGroup.load(tg.getId());
		if (oldTg != null) {
			tg.setCreateTime(oldTg.getCreateTime());
			tg.setCreator(oldTg.getCreator());
			tg.update();
		}
	}

	@Path("/testing/group/delete")
	public void deleteGroup(Request request) {

		int[] ids = Validator.toIntArray(request.getString("ids"));
		if (ids.length > 0) {

			for (int id : ids) {
				deleteGroup(id);
			}
		}
	}

	private void deleteGroup(int id) {

		// delete cases
		SessionFactory.getCurrentDB().update("delete from TestCase where testPointId in (select id from TestPoint where groupId=?)", id);
		// delete point
		SessionFactory.getCurrentDB().update("delete from TestPoint where groupId=?", id);
		// delete groups
		TestGroup.delete(id);
	}

	@Path("/testing/group/copy")
	public void copyGroup(Request request) {

		int fromId = request.getInt("from");

		TestGroup ftg = TestGroup.load(fromId);
		if (ftg == null) { return; }

		TestGroup tg = TestGroup.create(request);
		tg.setCreateTime(new Date());
		tg.setCreator(LoginUser.current().getUser().getId());
		tg.setProjectId(ftg.getProjectId());

		tg.save();

		Map<Integer, TestPoint> testPoints = new HashMap<>();
		List<TestPoint> points = TestPoint.list(-1, "where groupId=" + ftg.getId());
		// copy points
		for (TestPoint tp : points) {
			TestPoint npoint = new TestPoint();
			npoint.setCreateTime(new Date());
			npoint.setCreator(LoginUser.current().getUser().getId());
			npoint.setGroupId(tg.getId());
			npoint.setModuleId(tp.getModuleId());
			npoint.setProjectId(tp.getProjectId());
			npoint.setRemark(tp.getRemark());
			npoint.setName(tp.getName());
			npoint.save();
			testPoints.put(tp.getId(), npoint);
		}

		// copy cases
		List<TestCase> cases = null;
		if (StringUtils.equals("fail", request.getString("caseType"))) {
			cases = TestCase.list(-1, "where result=2 and testPointId in(select id from TestPoint where groupId=" + ftg.getId() + ")", SessionFactory.getCurrentDB());
		} else {
			cases = TestCase.list(-1, "where testPointId in(select id from TestPoint where groupId=" + ftg.getId() + ")", SessionFactory.getCurrentDB());
		}
		for (TestCase tc : cases) {
			TestCase ncase = new TestCase();
			ncase.setActual(tc.getActual());
			ncase.setCreator(LoginUser.current().getUser().getId());
			ncase.setCreateTime(new Date());
			ncase.setExpected(tc.getExpected());
			ncase.setInput(tc.getInput());
			ncase.setName(tc.getName());
			ncase.setRemark(tc.getRemark());

			TestPoint tp = testPoints.get(tc.getTestPointId());
			if (tp == null) {
				continue;
			}
			ncase.setTestPointId(tp.getId());
			ncase.save();
		}

		SessionFactory.getCurrentDB().update("update testpoint tp set tp.total=(select count(*) from testcase tc where tc.testPointId=tp.id) where tp.groupId=?", tg.getId());
	}
}
